var ffi = require("@system.ffi")
var dlfcn = require("@system.dlfcn")

function register(modulename, path, funcs) {
    var lib = dlfcn.dlopen(path, 1)
    var f = [];
    for (var i = 0; i < funcs.length; i++) {
        var item = funcs[i];
        var name = item[0];
        var signature = item[1];
        var funcPointer = dlfcn.dlsym(lib, name);
        f.push([name, funcPointer, signature])
    }
    return ffi.register(modulename, f)
}

var funcs = [
    ["PyImport_ImportModule", "ps"],
    ["PyObject_Str", "pp"],
    ["Py_Initialize", "v"],
    ["Py_Finalize", "v"],
    ["PyObject_CallObject", "ppp"],
    ["Py_IncRef", "vp"],
    ["Py_DecRef", "vp"],
    ["PyRun_SimpleString", "is"],
    ["PyObject_GetAttrString", "pps"],
    ["PyObject_HasAttrString", "ips"],
    ["Py_BuildValue", "pss"],
    ["PyTuple_New", "pi"],
    ["PyTuple_SetItem", "ipip"],
    ["PyTuple_Size", "ip"],
    ["PyTuple_GetItem", "ppi"],
    ["PyModule_GetDict", "pp"],
    ["PyUnicode_AsUTF8", "sp"],
    ["PyUnicode_FromString", "ps"],
    ["PyBytes_AsString", "sp"],
    ["PyLong_AsLong", "ip"],
    ["PyLong_FromLong", "pi"],
    ["PyFloat_AsDouble", "dp"],
    ["PyFloat_FromDouble", "pd"],
    ["PyList_New", "pi"],
    ["PyList_Size", "ip"],
    ["PyList_GetItem", "ppi"],
    ["PyList_SetItem", "ipip"],
    ["PyDict_New", "p"],
    ["PyDict_Keys", "pp"],
    ["PyDict_Size", "ip"],
    ["PyDict_Values", "pp"],
    ["PyDict_GetItem", "ppp"],
    ["PyDict_SetItem", "ippp"],
    ["PyDict_SetItemString", "ipsp"],
    ["PyImport_AddModule", "ps"],
    ["PyRun_StringFlags", "psippp"],
];

python = undefined

Py_file_input = 257

function init_python(libpath, paths) {
    isSuccess = register("@native.python", libpath, funcs);
    if (!isSuccess) {
        return false;
    }
    python = require("@native.python");
    python.Py_Initialize();
    python.PyRun_SimpleString("import sys");
    for (var i = 0; i < paths.length; i++) {
        var path = paths[i];
        var cmd = "sys.path.append('" + path + "')";
        console.log(cmd)
        console.log(python.PyRun_SimpleString(cmd));
    }
    python.exBulitins = "exBuiltin";
    return python
}

function destory() {
    python.Py_Finalize();
}

function addPath(path) {
    callmethod(python.exBulitins, "addPath", [path]);
}

function js_2_py(JsValue) {
    var ret = undefined;
    if (typeof JsValue == "number") {
        if (parseInt(JsValue) == JsValue) {
            ret = python.PyLong_FromLong(JsValue);
        } else {
            ret = python.PyFloat_FromDouble(JsValue);
        }
    } else if (typeof JsValue == "string") {
        ret = python.PyUnicode_FromString(JsValue);
    } else if (typeof JsValue == "list") {
        var size = JsValue.length;
        ret = python.PyList_New(size);
        for (var i = 0; i < size; i++) {
            var Js_Value = JsValue[i];
            var PyValue = js_2_py(Js_Value);
            python.PyList_SetItem(ret, i, PyValue);
        }
    } else if (typeof JsValue == "object") {
        ret = callmethod(python.exBulitins, "pyDict", [JSON.stringify(JsValue)]);
        python.Py_IncRef(ret)
    }
    return ret;
}

function runPyFunc(pyFunc, args) {
    // 调用python的函数,参数为js的List类型(*args, 自动转换为python元组)
    var d = callfunc(pyFunc, args);
    // 返回值转换成js数据类型
    var PyObject_Type = callmethod(python.exBulitins, "typeof", [d]);
    var type = python.PyUnicode_AsUTF8(PyObject_Type);
    d = py_2_js(d, type);
    return d;
}

function pyfun_2_jsfun(pyFunc) {
    argc = py_2_js(callmethod(python.exBulitins, "argc", [pyFunc]), 'int');
    var ret = undefined;
    if (argc == 0) {
        ret = function() {
            args = [];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 1) {
        ret = function(arg) {
            args = [arg];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 2) {
        ret = function(arg1, arg2) {
            args = [arg1, arg2];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 3) {
        ret = function(arg1, arg2, arg3) {
            args = [arg1, arg2, arg3];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 4) {
        ret = function(arg1, arg2, arg3, arg4) {
            args = [arg1, arg2, arg3, arg4];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 5) {
        ret = function(arg1, arg2, arg3, arg4, arg5) {
            args = [arg1, arg2, arg3, arg4, arg5];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 6) {
        ret = function(arg1, arg2, arg3, arg4, arg5, arg6) {
            args = [arg1, arg2, arg3, arg4, arg5, arg6];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 7) {
        ret = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
            args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 8) {
        ret = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {
            args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8];
            return runPyFunc(pyFunc, args);
        }
    } else if (argc == 9) {
        ret = function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {
            args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9];
            return runPyFunc(pyFunc, args);
        }
    }

    return ret;
}

function py_2_js(PyObject, type) {
    var ret = PyObject;
    if (type == "int") {
        ret = python.PyLong_AsLong(PyObject);
    } else if (type == "float") {
        ret = python.PyFloat_AsDouble(PyObject);
    } else if (type == "str") {
        ret = python.PyUnicode_AsUTF8(PyObject);
    } else if (type == "tuple") {
        ret = [];
        var size = python.PyTuple_Size(PyObject);
        for (var i = 0; i < size; i++) {
            var PyObject_Value = python.PyTuple_GetItem(PyObject, i);
            var PyObject_Type = callmethod(python.exBulitins, "typeof", [PyObject_Value]);
            var type = python.PyUnicode_AsUTF8(PyObject_Type);
            var jsValue = py_2_js(PyObject_Value, type);
            ret.push(jsValue);
        }
    } else if (type == "list") {
        ret = [];
        var size = python.PyList_Size(PyObject);
        for (var i = 0; i < size; i++) {
            var PyObject_Value = python.PyList_GetItem(PyObject, i);
            var PyObject_Type = callmethod(python.exBulitins, "typeof", [PyObject_Value]);
            var type = python.PyUnicode_AsUTF8(PyObject_Type);
            var jsValue = py_2_js(PyObject_Value, type);
            ret.push(jsValue);
        }
    } else if (type == "dict") {
        ret = {};
        var size = python.PyDict_Size(PyObject);
        var Keys = python.PyDict_Keys(PyObject);
        var Values = python.PyDict_Values(PyObject);
        for (var i = 0; i < size; i++) {
            var PyObject_Key = python.PyList_GetItem(Keys, i);
            var key = python.PyUnicode_AsUTF8(PyObject_Key);
            var PyObject_Value = python.PyDict_GetItem(PyObject, PyObject_Key);
            var PyObject_Type = callmethod(python.exBulitins, "typeof", [PyObject_Value]);
            var type = python.PyUnicode_AsUTF8(PyObject_Type);
            var jsValue = py_2_js(PyObject_Value, type);
            ret[key] = jsValue;
        }
    } else if (type == "function") {
        ret = pyfun_2_jsfun(PyObject);
    } else if (type == "None") {
        ret = undefined;
    }
    return ret;
}

function callfunc(pyFunc, args) {
    var argsLength = args.length;
    if (argsLength == 0 || args == undefined) {
        pyTupeArgs = python.PyTuple_New(0);
    } else {
        pyTupeArgs = python.PyTuple_New(argsLength);
        for (var i = 0; i < argsLength; i++) {
            var arg = args[i];
            if (typeof arg == "invokable") {
                python.PyTuple_SetItem(pyTupeArgs, i, arg);
            } else {
                pyArg = js_2_py(arg);
                flag = python.PyTuple_SetItem(pyTupeArgs, i, pyArg)
            }
        }
    }
    var ret = python.PyObject_CallObject(pyFunc, pyTupeArgs);
    return ret
}

function callmethod(module, func, args) {
    var pModule = python.PyImport_ImportModule(module);
    var pyFunc = python.PyObject_GetAttrString(pModule, func);
    var ret = callfunc(pyFunc, args)
    return ret
}

function pprint(obj) {
    callmethod("builtins", "print", [obj]);
}

function dir(obj) {
    callmethod("builtins", "dir", [obj]);
}

function py_import(name) {
    var obj = {};
    console.log("//////////")
    console.log(name)
    console.log(typeof name)
    var module = python.PyImport_ImportModule(name)
    var pmetainfo = callmethod(python.exBulitins, "metainfo", [module]);
    var metainfo_string = python.PyUnicode_AsUTF8(pmetainfo);
    var metainfo = JSON.parse(metainfo_string);
    for (var i = 0; i < metainfo.length; i++) {
        var info = metainfo[i];
        var key = info['key'];
        var type = info['type'];
        var value = python.PyObject_GetAttrString(module, key);
        value = py_2_js(value, type);
        obj[key] = value;
    }
    return obj;
}